package com.api.controller;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.CertAlipayRequest;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeAppPayModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeAppPayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeAppPayResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.aliyuncs.exceptions.ServerException;
import com.api.config.AliPayConfig;
import com.common.annotation.PassToken;
import com.common.apiResult.ApiController;
import com.common.apiResult.ApiResult;
import com.common.config.AlipayProperties;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @ClassName AliPayController
 * @Description 阿里云支付
 * @Date 2020/10/11 18:16
 * Create By Renbowen
 */
@RestController
@RequestMapping(value = "/api/aliPay")
@Slf4j
public class AliPayController extends ApiController {

    // 支付宝支付配置
    private final AlipayProperties alipayProperties;

    private final AliPayConfig aliPayConfig;

    public AliPayController(AlipayProperties alipayProperties, AliPayConfig aliPayConfig) {
        this.alipayProperties = alipayProperties;
        this.aliPayConfig = aliPayConfig;
    }

    @RequestMapping(value = "/toPayByAli", method = RequestMethod.POST)
    @PassToken
    public ApiResult toPayByAli() throws AlipayApiException, FileNotFoundException, ServerException {
        //构造client
        AlipayClient alipayClient = aliPayConfig.alipayClient();

        //实例化具体API对应的request类,类名称和接口名称对应,当前调用接口名称：alipay.trade.app.pay
        AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
        //SDK已经封装掉了公共参数，这里只需要传入业务参数。以下方法为sdk的model入参方式(model和biz_content同时存在的情况下取biz_content)。
        AlipayTradeAppPayModel model = new AlipayTradeAppPayModel();
        model.setBody("我是测试数据");
        model.setSubject("App支付测试Java");
        model.setOutTradeNo(generateOrderSerialNumber(1L));
        model.setTimeoutExpress("30m");
        model.setTotalAmount("0.01");
        model.setProductCode("QUICK_MSECURITY_PAY");
        request.setBizModel(model);
        request.setNotifyUrl(alipayProperties.getNotifyUrl() + "/api/aliPay/notifyAliPay");
        try {
            //这里和普通的接口调用不同，使用的是sdkExecute
            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
            System.out.println(response.getBody()); //就是orderString 可以直接给客户端请求，无需再做处理。
            return ApiResult.ok(response.getBody());
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return ApiResult.fail(e.getMessage());
        }
    }

    /**
     * 阿里云支付回调
     * @param request
     * @param response
     * @return
     * @throws FileNotFoundException
     * @throws UnsupportedEncodingException
     * @throws ServerException
     */
    @RequestMapping(value = "/notifyAliPay", method = RequestMethod.POST)
    @PassToken
    public String notifyAliPay(HttpServletRequest request, HttpServletResponse response) throws FileNotFoundException, UnsupportedEncodingException, ServerException {
        // 一定要验签，防止黑客篡改参数
        Map<String, String[]> parameterMap = request.getParameterMap();
        StringBuilder notifyBuild = new StringBuilder("/****************************** 支付宝支付回调 ******************************/\n");
        parameterMap.forEach((key, value) -> notifyBuild.append(key).append("=").append(value[0]).append("\n"));
        log.info(notifyBuild.toString());
        log.info("通知回调：AlipayController-notify-日志信息信息：" + request.getParameterMap().toString());

        boolean flag = rsaCheckV1(request);
        if (flag) {
            /*
             *
             * 商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号， 并判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额）， 同时需要校验通知中的seller_id（或者seller_email)
             * 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email），
             *
             * 上述有任何一个验证不通过，则表明本次通知是异常通知，务必忽略。 在上述验证通过后商户必须根据支付宝不同类型的业务通知，正确的进行不同的业务处理，并且过滤重复的通知结果数据。
             * 在支付宝的业务通知中，只有交易通知状态为TRADE_SUCCESS或TRADE_FINISHED时，支付宝才会认定为买家付款成功。
             */

            // 交易状态
            String tradeStatus = new String(request.getParameter("trade_status").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            // 商户订单号
            String outTradeNo = new String(request.getParameter("out_trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            // 支付宝交易号
            String tradeNo = new String(request.getParameter("trade_no").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            // 付款金额
            String totalAmount = new String(request.getParameter("total_amount").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            String buyerLogonId = new String(request.getParameter("buyer_logon_id").getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
            // TRADE_FINISHED(表示交易已经成功结束，并不能再对该交易做后续操作);
            // TRADE_SUCCESS(表示交易已经成功结束，可以对该交易做后续操作，如：分润、退款等);
            if ("TRADE_SUCCESS".equals(tradeStatus)){
                //TODO 交易成功->执行操作
                System.out.println("交易状态:"+tradeStatus);
                System.out.println("商户订单号:"+outTradeNo);
                System.out.println("支付宝交易号:"+tradeNo);
                System.out.println("付款金额:"+totalAmount);
                System.out.println("buyerLogonId:"+buyerLogonId);
            }
            return "success";
        }
        return "fail";
    }

    /**
     * 支付宝退款
     * @param tradeNo 支付宝流水号
     * @param outTradeNo 商户订单号
     * @return
     * @throws FileNotFoundException
     * @throws AlipayApiException
     */
    @RequestMapping(value = "/refundByAliPay",method = RequestMethod.POST)
    @PassToken
    public ApiResult refundByAliPay(String tradeNo,String outTradeNo) throws AlipayApiException,FileNotFoundException {
        //构造client
        AlipayClient alipayClient = aliPayConfig.alipayClient();
        AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest();
        AlipayTradeRefundModel model = new AlipayTradeRefundModel();
        model.setTradeNo(tradeNo);
        model.setOutTradeNo(outTradeNo);
        model.setRefundAmount("0.01");
        model.setRefundReason("商品退款");
        refundRequest.setBizModel(model);

        try {
            AlipayTradeRefundResponse response = alipayClient.certificateExecute(refundRequest);

            if (response.isSuccess() && "Y".equalsIgnoreCase(response.getFundChange())) {
                // 成功
                return ApiResult.ok(response);
            } else {
                // 失败
                return ApiResult.fail("支付宝退款失败！");
            }
        } catch (AlipayApiException e) {
            e.printStackTrace();
            log.error(" - 支付宝退款异常 - ", e);
            return ApiResult.fail("支付宝退款失败！");
        }
    }

    /**
     * 校验签名 证书模式
     * @param request
     * @return
     */
    public boolean rsaCheckV1(HttpServletRequest request) throws FileNotFoundException {
        // 获取支付宝POST过来反馈信息
        Map<String, String> params = new HashMap<>();
        Map requestParams = request.getParameterMap();
        for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
            String name = (String) iter.next();
            String[] values = (String[]) requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
            }
            // 乱码解决，这段代码在出现乱码时使用。
            // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        // 切记alipaypublickey是支付宝的公钥，请去open.alipay.com对应应用下查看。
        // boolean AlipaySignature.rsaCertCheckV1(Map<String, String> params, String publicKeyCertPath, String charset,String signType)
        boolean flag = false;
        try {
            flag = AlipaySignature.rsaCertCheckV1(params, ResourceUtils.getFile(alipayProperties.getAlipayPublicCertPath()).getAbsolutePath(), alipayProperties.getCharset(),
                    alipayProperties.getSignType());
            return flag;
        } catch (AlipayApiException e) {
            e.printStackTrace();
            return false;
        }
    }
}
